#ifndef _MAIN_H_

#define _MAIN_H_

#define KEY_FILE_IDENTIFICATION 357784
#define ASCII_PRINTABLE_CHAR_START 0x20
#define ASCII_PRINTABLE_CHAR_END 0x7E
#define TOTAL_SYMBOL_NUMBER (ASCII_PRINTABLE_CHAR_END - ASCII_PRINTABLE_CHAR_START + 1)
#define MAX_BITCODE_LENGTH (4 * BITS_PRE_BYTE)

// 1 if is a leaf, 0 otherwise
#define HuffTreeIsLeaf(t) *((char *)((int *)t + 1))
// 1 if is a leaf, 0 otherwise
#define DecodeHuffTreeIsLeaf(t) *((char *)t)

struct BitCodeStruct{
	int code_length;
	unsigned char *code_array;
};

// [weight] field has to be the first member in consistency with our
// implementation of priority queue and type field has to be the second
struct HuffmanTreeLeaveStruct{
	int weight;
	// 1 indicates a leaf, 0 indicates a interior node
	char type;
	unsigned char symbol;
};

// [weight] field has to be the first member in consistency with our
// implementation of priority queue and type field has to be the second
struct HuffmanTreeInteriorNodeStruct{
	int weight;
	// 1 indicates a leaf, 0 indicates a interior node
	char type;
	void *leftChild, *rightChild;
};

// we don't need the [weight] field
// when decoding. [type] has to be
// the first member
struct DecodeHuffTreeLeaf{
	// 1 indicates a leaf, 0 indicates a interior node
	char type;
	char symbol;
};

// we don't need the [weight] field
// when decoding
struct DecodeHuffTreeInteriorNodeStruct{
	// 1 indicates a leaf, 0 indicates a interior node
	char type;
	void *leftChild, *rightChild;
};

// [bitcode_index] field is required to be the first member
struct DecodeInfoStruct{
	// bitcode_index is calculated as 2^l - 2 + D(B)
	// where l is the length of the bitcode, D(B) is
	// the decimal value of the bitcode. For example,
	// for bitcode 010, the index is 2^3 - 2 + 2 = 8
 	unsigned int bitcode_index;
	char corresponding_symbol;
};

typedef struct BitCodeStruct BitCode;
typedef struct HuffmanTreeLeaveStruct HuffmanTreeLeave, SymbolEncodeInfo;
typedef struct HuffmanTreeInteriorNodeStruct HuffmanTreeInteriorNode, HuffmanTree;
typedef struct DecodeInfoStruct DecodeInfo;

typedef struct DecodeHuffTreeInteriorNodeStruct DecodeHuffTreeInteriorNode, DecodeHuffTree;
typedef struct DecodeHuffTreeLeaf DecodeHuffTreeLeaf;

typedef Heap *HuffmanForest;
typedef BitCode **EncodeTable;

HuffmanForest InitializeHuffmanForest(int *freq_list);

HuffmanTree *ConstructHuffmanTree(HuffmanForest forest);

void _EncodeThoughHuffmanTree_Rec(void *tree, BitStack *stack, EncodeTable table);

EncodeTable EncodeThroughHuffmanTree(HuffmanTree *tree);

void PrintHuffTree(void *t);

void ShowEncodeTable(EncodeTable table);

void WriteDecodeHuffmanTreeToFile(void *t, FILE *fp);

void *ReadDecodeHuffmanTreeFromFile(FILE *fp);

void PrintDecodeHuffTree(void *t);

void DisposeHuffmanTree(void *t);

void DisposeHuffmanForest(HuffmanForest f);

void DisposeEncodeTable(EncodeTable table);

void DisposeDecodeHuffTree(void *t);

void EncodeFile(FILE *fp_in, FILE *fp_out, EncodeTable encode_table);

void DecodeFile(FILE *fp_in, FILE *fp_out, DecodeHuffTree *decode_tree);

void StartSamplingProcess(char *input_file_name, char *output_file_name);

void StartEncodeProcess(char *input_file_name, char *output_file_name, char *key_file_name, int *freq_list);

void StartDecodeProcess(char *input_file_name, char *output_file_name, char *key_file_name);

void HandleParameters(int argc, char ** argv);

#endif
